Analyzing the dynamic behavior and performance of a complex software system is a difficult task. Typically, a software system is analyzed by gathering data about the system at each system call and post-analyzing such data using a post-processing utility. One example of a conventional means for performing software analysis is a tracing framework.
In a typical architecture of a tracing framework, system calls (or trace points) are defined by a user in the software system's source code. In a tracing framework that allows for statically defined trace points, a call to a particular probe, which may be used to provide a desired type of analysis info, is inserted at each trace point. Generally, the syntax of this call is based on a code, where the code is defined using a variant of the Code Sample 1 below.
Code Sample 1#define TRACE (name)if (_tracing_enabled)_tracing_function(#name)
Code Sample 1 shows a TRACE function passing the parameter name, which identifies a name of a desired probe (e.g., the probe to be enabled). The conditional statement _tracing_enabled identifies whether tracing analysis is currently enabled for the probe identified as name. The function _tracing_function identifies a call to a particular probe identified as name. In essence, when tracing analysis is enabled, the code shown in Code Sample 1 initiates a call at each trace point so tracing may be executed by the identified probe on the relevant code.
Consequently, during compilation of the code shown in Code Sample 1, several instructions may be generated for each trace point even when tracing analysis is disabled. The number of these instructions, which include at least a load instruction, a compare instruction, and a branch, can add up quickly. Overall system performance may be affected by the unwarranted execution of these numerous instructions.